/*
 * Created: Author: Aléx Carvalho / Date: Mar 30, 2015
 * 
 * Changed: $Author: ac280593@gmail.com $ / $Date: 2015-06-11 01:05:53 -0300 (Thu, 11 Jun 2015) $
 */
package com.cpm;

import com.cpm.entities.Competence;
import com.cpm.entities.types.IndicatorDirectionType;
import com.cpm.business.PerformanceCalculator;
import com.cpm.entities.Folder;
import com.cpm.entities.Indicator;
import com.cpm.entities.StrategicPlan;
import com.cpm.entities.types.IndicatorModeType;
import com.cpm.wrapper.CompetenceList;
import com.cpm.wrapper.Performance;
import com.cpm.wrapper.Result;
import com.cpm.wrapper.SolidObjects;
import com.cpm.wrapper.Target;
import com.cpm.wrapper.exception.SystemException;
import java.util.Collection;

public class PerformanceCalculatorImpl implements PerformanceCalculator {

    private final class ResultAndTarget {

        final Result result;
        final Target target;

        public ResultAndTarget(Result result, Target target) {
            this.result = result;
            this.target = target;
        }

        private ResultAndTarget() {
            this.result = null;
            this.target = null;
        }
    }

    @Override
    public Performance calculatePerformanceStrategicPlan(StrategicPlan strategicPlan) {
        return ServiceLocator.getStrategicPlanRepository().calculePerformance(strategicPlan);
    }

    @Override
    public Performance calculatePerformanceFolder(Folder folder) {
        return ServiceLocator.getFolderRepository().calculePerformance(folder);
    }

    @Override
    public Performance calculatePerformanceCompetence(Competence competence) {
        SolidObjects.notNull(competence);

        ResultAndTarget resultAndTarget = getResultAndTargetOfCompetence(competence);

        if (resultAndTarget != null) {
            Indicator indicator = competence.getIndicator();

            Double result = resultAndTarget.result.getValue();
            Double target = resultAndTarget.target.getValue();

            Double d = indicator.getDirection() == IndicatorDirectionType.DIRECT
                    ? calculatePerformanceIndicatorDirect(result, target) * 100
                    : calculatePerformanceIndicatorInverse(result, target) * 100;

            return new Performance(d.floatValue());
        }

        return null;
    }

    private ResultAndTarget getResultAndTargetOfCompetence(Competence c) {
        Indicator indicator = c.getIndicator();
        CompetenceList competenceList = indicator.getCompetenceList();

        if (c.getTarget() == null || c.getTarget().getValue() == null
                || c.getResult() == null || c.getResult().getValue() == null) {
            return null;
        }

        if (indicator.getMode() == IndicatorModeType.INDIVIDUAL) {
            return new ResultAndTarget(c.getResult(), c.getTarget());
        }

        else if (indicator.getMode() == IndicatorModeType.AVERAGE) {
            Collection<Competence> competences = competenceList.getCompetencesSmaller(c);

            Result result = c.getResult();
            Target target = c.getTarget();
            double count = 1;
            for (Competence comp : competences) {
                if (comp.getResult() != null && comp.getTarget() != null) {
                    result = result.plus(comp.getResult());
                    target = target.plus(comp.getTarget());
                    count++;
                }
            }

            result = result.divideBy(count);
            target = target.divideBy(count);

            if (result != null && target != null) {
                return new ResultAndTarget(result, target);
            }
        }

        else if (indicator.getMode() == IndicatorModeType.CUMULATIVE) {
            Collection<Competence> competences = competenceList.getCompetencesSmaller(c);

            Result result = c.getResult();
            Target target = c.getTarget();
            for (Competence comp : competences) {
                if (comp.getResult() != null && comp.getTarget() != null) {
                    result = result.plus(comp.getResult());
                    target = target.plus(comp.getTarget());
                }
            }

            if (result != null && target != null) {
                return new ResultAndTarget(result, target);
            }
        }

        else {
            throw new SystemException("IndicatorMode not found! " + indicator.getMode());
        }

        return null;
    }

    private Double calculatePerformanceIndicatorDirect(Double result, Double target) {

        if (target > 0.0) {
            return result / target;
        }

        if (target < 0.0) {
            return result == 0.0 ? 2 : 2 - (result / target);
        }

        return result > 0.0 ? 2.0 : (result < 0.0 ? 0.0 : 1.0);
    }

    private Double calculatePerformanceIndicatorInverse(Double result, Double target) {
        if (target > 0.0) {
            return result < 0 ? (1 + (target - result)) : (result == 0.0 ? 2 : target / result);
        }
        if (target < 0.0) {
            return 1 + ((result - target) / target);
        }

        return result < 0.0 ? 2.0 : (result > 0.0 ? 0.0 : 1.0);
    }
}
